package com.alex.core.logUtils;

/**
 * Created by xc on 2018/6/23.
 */

import com.alibaba.fastjson.JSON;
import javassist.*;
import javassist.bytecode.CodeAttribute;
import javassist.bytecode.LocalVariableAttribute;
import javassist.bytecode.MethodInfo;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;

/**
 * Created by xc on 2018/6/23.
 */
@Aspect
@Service
public class SysLogAspect {
    private static final Logger LOGGER = LoggerFactory.getLogger(SysLogAspect.class);

    /**
     * 定义拦截规则：拦截所有@QueryCache注解的方法。
     */
    @Pointcut("@annotation(SysLog)")
    public void queryCachePointcut(){}
    /**
     * 拦截器具体实现
     * @return
     * @throws Throwable
     */
    @Around("queryCachePointcut()")
    public Object queryInterceptor(ProceedingJoinPoint joinPoint) throws Throwable {
        long beginTime = System.currentTimeMillis();
        LOGGER.info("AOP 查询缓存切面处理 >>>> start ");
        // 得到类名、方法名和参数
        String clazzName = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        // 根据类名、方法名和参数生成Key
        LOGGER.info("key参数: " + clazzName + "." + methodName);
        // 得到被代理的方法
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        // 得到被代理方法的返回值类型
        Class returnType = ((MethodSignature) joinPoint.getSignature()).getReturnType();
        // result是方法的最终返回结果
        Object result = null;
        //获得参数名
        Map<String,Object > nameAndArgs = getFieldsName(this.getClass(), clazzName, methodName,args);
        for(String name : nameAndArgs.keySet()){
            Object data = getFieldValueByName(name, result);
        }
        // 调用数据库查询方法
        result = joinPoint.proceed(args); //将 HTTP 请求转交给相应的控制器处理
        try {
            // 序列化查询结果
            String json = JSON.toJSONString(result);
        } catch (Throwable e) {
            LOGGER.error("返回参数解析异常",e);
        }
        LOGGER.info("AOP 日志处理切面处理 >>>> end 耗时：" + (System.currentTimeMillis() - beginTime));
        return result;
    }

    private Map<String,Object> getFieldsName(Class cls, String clazzName, String methodName, Object[] args) throws NotFoundException {
        Map<String,Object > map=new HashMap<String,Object>();

        ClassPool pool = ClassPool.getDefault();
        //ClassClassPath classPath = new ClassClassPath(this.getClass());
        ClassClassPath classPath = new ClassClassPath(cls);
        pool.insertClassPath(classPath);

        CtClass cc = pool.get(clazzName);
        CtMethod cm = cc.getDeclaredMethod(methodName);
        MethodInfo methodInfo = cm.getMethodInfo();
        CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
        LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);
        if (attr == null) {
            // exception
        }
        // String[] paramNames = new String[cm.getParameterTypes().length];
        int pos = Modifier.isStatic(cm.getModifiers()) ? 0 : 1;
        for (int i = 0; i < cm.getParameterTypes().length; i++){
            map.put( attr.variableName(i + pos),args[i]);//paramNames即参数名
        }
        //Map<>
        return map;
    }

    /**
     * 根据属性名获取属性值 
     * */
    private Object getFieldValueByName(String fieldName, Object o) {
        try {
            String firstLetter = fieldName.substring(0, 1).toUpperCase();
            String getter = "get" + firstLetter + fieldName.substring(1);
            Method method = o.getClass().getMethod(getter, new Class[] {});
            Object value = method.invoke(o, new Object[] {});
            return value;
        } catch (Exception e) {
            
            return null;
        }
    }
}
